package com.yeyks.commonReference.service.consumer.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.yeyks.common.base.PageInfo;
import com.yeyks.common.constants.ConfigKey;
import com.yeyks.common.constants.CouponConst;
import com.yeyks.common.constants.MessageTemplate;
import com.yeyks.common.em.MessageNoticeType;
import com.yeyks.common.em.UserCouponInfoSource;
import com.yeyks.common.em.UserCouponInfoStatus;
import com.yeyks.common.exception.ServiceException;
import com.yeyks.common.utils.DateUtils;
import com.yeyks.common.utils.FormatUtils;
import com.yeyks.common.utils.ObjectConvertUtil;
import com.yeyks.common.utils.RedisUtil;
import com.yeyks.common.utils.apputil.rediskey.RedisKey;
import com.yeyks.commonReference.append.rocketmq.service.producer.MQProducerService;
import com.yeyks.commonReference.service.config.ConfigService;
import com.yeyks.commonReference.service.consumer.MessageNoticeTemplateService;
import com.yeyks.commonReference.service.consumer.UserCouponInfoService;
import com.yeyks.commonReference.service.order.OrderHeadService;
import com.yeyks.consumer.dal.dao.UserCouponInfoMapper;
import com.yeyks.consumer.dal.domain.UserCouponInfo;
import com.yeyks.consumer.dal.domain.UserCouponPush;
import com.yeyks.consumer.dal.domain.UserInfo;
import com.yeyks.consumer.dto.*;
import com.yeyks.consumer.param.UserCouponInfoSelectUserCouponListParam;
import com.yeyks.consumer.param.coupon.UserCouponPushParam;
import com.yeyks.consumer.service.MessageNoticeService;
import com.yeyks.consumer.service.UserCouponPushService;
import com.yeyks.other.dal.dao.CouponInfoMapper;
import com.yeyks.other.dal.domain.CouponInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
 * <p>
 * 领取券表 服务实现类
 * </p>
 *
 * @author yun
 * @since 2019-06-25
 */
@Service
@Slf4j
public class UserCouponInfoServiceImpl extends ServiceImpl<UserCouponInfoMapper, UserCouponInfo> implements UserCouponInfoService {
    @Autowired
    private UserCouponInfoMapper userCouponInfoMapper;
    @Autowired
    private CouponInfoMapper couponInfoMapper;
    @Autowired
    private MessageNoticeService noticeService;
    @Autowired
    private MessageNoticeTemplateService templateService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ConfigService configService;
    @Autowired
    private OrderHeadService orderHeadService;
    @Autowired
    private MQProducerService mqProducerService;
    @Autowired
    private UserCouponPushService userCouponPushService;

    private static final String redisUserCouponOperating = "redisLock:UserCouponService:giveBagGift";

//    /**
//     * 根据一定条件查询当前用户的优惠券
//     *
//     * @param id
//     * @param useStatus 是否已经使用, 默认1未使用，2已使用，3已过期
//     * @param price 满减价格
//     * @param pageNum
//     * @param pageSize
//     * @return
//     */
//    @Override
//    public Map<String, Object> selectUserCouponList(Integer id, Integer useStatus, Integer price, Integer pageNum, Integer pageSize) {
//        PageInfo pageInfo = PageInfo.of(pageNum, pageSize);
//        Map<String, Object> map = new HashMap<>();
//        map.put("id", id);
//        map.put("useStatus", useStatus);
//        map.put("price", price);
//        map.put("offset", pageInfo.getOffset());
//        map.put("limit", pageInfo.getLimit());
////        List<Map> list = userCouponInfoMapper.selectUserCouponList(map);
////        Long count = userCouponInfoMapper.selectUserCouponListCount(map);
//        List<Map> list = userCouponInfoMapper.selectUserCouponListV2(map);
//        Long count = userCouponInfoMapper.selectUserCouponListCountV2(map);
//        map.clear();
//        map.put("list", list);
//        map.put("count", count);
//        return map;
//    }


    /**
     * 通过count，减少券的剩余数，并增加券的领取数
     *
     * @param count      领取的数量
     * @param couponInfo
     * @return
     */
    @Override
    public Integer updateCount(Integer count, CouponInfo couponInfo) {
        Integer totalCount = couponInfo.getCount();
        if (totalCount == null || totalCount <= 0) {
            log.info("10元新用户券总数为空");
            return 0;
        }
        Integer getCount = couponInfo.getGetCount();
        Integer remainCount;
        getCount += count;
        if (totalCount < getCount) {
            log.info("10元新用户券已领取数大于总数");
            return 0;
        }
        remainCount = totalCount - getCount;
        couponInfo.setGetCount(getCount);
        couponInfo.setRemainCount(remainCount);
        return couponInfoMapper.updateByPrimaryKey(couponInfo);
    }

    /**
     * 修改券的使用状态
     *
     * @param couponId
     * @return
     */
    @Override
    public Integer updateUsed(Integer couponId, Integer useStatus) {
        return userCouponInfoMapper.updateUsedStatus(couponId, useStatus);
    }

    /**
     * 通过userCouponId修改优惠券的使用状态
     *
     * @param userCouponId
     * @param use
     * @return
     */
    @Override
    public Integer updateCouponUseById(Integer userCouponId, String orderNO, Boolean use) {
        return userCouponInfoMapper.updateCouponUseById(userCouponId, orderNO, use);

    }

    /**
     * 通过券id和用户id查询该用户未使用的券
     *
     * @param couponId
     * @param id
     * @return
     */
    @Override
    public UserCouponInfo selectUserCoupon(Integer couponId, Integer id) {
        return userCouponInfoMapper.selectUserCoupon(couponId, id);
    }

    /**
     * 通过主键id查询用户券对象
     *
     * @param id
     * @return
     */
    @Override
    public UserCouponInfo selectUserCouponById(Integer id) {
        return userCouponInfoMapper.selectByPrimaryKey(id);
    }

    private UserCouponInfo getUserCouponInfo(Integer userId, Integer couponId, Integer duration) {
        UserCouponInfo userCouponInfo = new UserCouponInfo();
        userCouponInfo.setCouponId(couponId);
        userCouponInfo.setUserId(userId);
        userCouponInfo.setUsed(0);
        if (duration == null || duration == 0) {
            //180天
            duration = 180;
        }
        Date nowDate = new Date();
        Date date = DateUtils.afterDaysToDate(nowDate, Long.valueOf(duration));
        userCouponInfo.setGmtCreate(nowDate);
        userCouponInfo.setGmtModified(nowDate);
        userCouponInfo.setOverTime(date);
        return userCouponInfo;
    }

    /**
     * 共享读写锁
     */
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * 根据一定条件查询当前用户的优惠券(V2)
     *
     * @param userId
     * @param useStatus 是否已经使用, 默认1未使用，2已使用，3已过期
     * @param price     满减价格
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public Map<String, Object> selectUserCouponList(UserCouponInfoSelectUserCouponListParam param) {
        Integer userId = param.getUserId();
        Integer price = param.getPrice();
        PageInfo pageInfo = PageInfo.of(param.getPageNum(), param.getPageSize());
        Map<String, Object> map = new HashMap<>();
        map.put("id", userId);
        map.put("useStatus", param.getUseStatus());
        map.put("price", price);
        map.put("type", param.getType());
        map.put("offset", pageInfo.getOffset());
        map.put("limit", pageInfo.getLimit());
        //判断是否是jip酒店,是否可用大礼包券
//        if(Objects.nonNull(param.getHotelId())){
//            Boolean type8 = orderHeadService.checkHotelCanBeUseCouponInfoType8(param.getHotelId());
//            map.put("type8",type8);
//        }
        List<UserCouponResultDTO> userCouponResults = baseMapper.selectUserCouponListV2(map);
        Long count = baseMapper.selectUserCouponListCountV2(map);
        map.clear();
        map.put("list", disposeResultV2(userCouponResults));
        map.put("count", count);
        enableUseCoupon(userId, price, map);
        return map;
    }

    //判断是否可用优惠券
    private void enableUseCoupon(Integer userId, Integer price, Map<String, Object> map) {
        if (Objects.nonNull(price)) {
            Boolean enableUse = orderHeadService.checkOrderUserCouponInfoCount(userId);
            map.put("enableUse", enableUse);
        }
    }

    private List<UserCouponResult> disposeResult(List<UserCouponResult> userCouponResults) {
        for (UserCouponResult userCouponResult : userCouponResults) {
            if (userCouponResult.getType() != null) {
                if (userCouponResult.getType().equals(CouponConst.recommend)
                        || userCouponResult.getType().equals(CouponConst.free)) {
                    userCouponResult.setPrice(userCouponResult.getCoupon_value());
                }
            }
        }
        return userCouponResults;
    }

    private List<UserCouponResultDTO> disposeResultV2(List<UserCouponResultDTO> userCouponResults) {
        for (UserCouponResultDTO userCouponResult : userCouponResults) {
            if (userCouponResult.getType() != null) {
                if (userCouponResult.getType().equals(CouponConst.recommend)
                        || userCouponResult.getType().equals(CouponConst.free)) {
                    userCouponResult.setPrice(userCouponResult.getCouponValue());
                }
            }
        }
        return userCouponResults;
    }

    /**
     * 给用户发放两张新人红包券
     *
     * @param userId
     */
    @Override
    @Transactional
    public Boolean createNewCoupon(Integer userId) {
        if (Objects.equals(Convert.toInt(configService.getValue(ConfigKey.COUPON_NEW_USER)), 0)) {
            log.info("设置为不发放新人券");
            return false;
        }
        //查询券
        List<CouponInfo> couponInfos = couponInfoMapper.selectList(
                new QueryWrapper<CouponInfo>()
                        .eq(CouponInfo.TYPE, 11)
                        .eq(CouponInfo.STATUS, 2)
        );
        //遍历券
        if (CollectionUtil.isNotEmpty(couponInfos)) {
            for (CouponInfo couponInfo : couponInfos) {
                //判断能不能发
                if (couponInfo.getRemainCount() == null || couponInfo.getRemainCount() <= 0) {
                    log.info("新用户券剩余数量为空:{}", JSONUtil.toJsonStr(couponInfo));
                    continue;
                }
                //查询该用户是否已经领取过该券
                UserCouponInfo userCouponInfo = baseMapper.selectOne(
                        new QueryWrapper<UserCouponInfo>()
                                .eq(UserCouponInfo.USER_ID, userId)
                                .eq(UserCouponInfo.COUPON_ID, couponInfo.getId())
                                .last("limit 1")
                );
                if (Objects.nonNull(userCouponInfo)) {
                    log.info("已领取过该券userId = {},coupon = {}", userId, JSONUtil.toJsonStr(couponInfo));
                    continue;
                }
                userCouponInfo = getUserCouponInfo(userId, couponInfo.getId(), couponInfo.getDuration());
                userCouponInfo.setCouponValue(couponInfo.getPrice());
                baseMapper.insert(userCouponInfo);
                //新用户券通知记录
                registMessage(couponInfo.getPrice(), userId, 1);
                //修改券数量
                updateCount(1, couponInfo);
            }
        }
        //查询新人券(type=1 and price = 1000)
//        CouponInfo couponInfo = couponInfoMapper.selectNewCoupon(1, 1000);
//        if (couponInfo == null) {
//            log.info("没有合适的10元新用户券");
//            return false;
//        }
//        if (couponInfo.getRemainCount() == null || couponInfo.getRemainCount() <= 0) {
//            log.info("10元新用户券剩余数量为空");
//            return false;
//        }
//        //查询该用户是否已经领取过该券
//        Integer count = baseMapper.countOfUserIdCouponId(userId, couponInfo.getId());
//        if (count >= couponInfo.getCountPerUser()) {
//            log.info("已领取过该券userId = {},couponId = {}", userId, couponInfo.getId());
//            return false;
//        }
//        UserCouponInfo userCouponInfo = getUserCouponInfo(userId, couponInfo.getId(), couponInfo.getDuration());
//        userCouponInfo.setCouponValue(couponInfo.getPrice());
//        if (couponInfo.getCountPerUser() >= 2 && count == 0) {
//            for (int i = 0; i < 2; i++) {
//                baseMapper.insert(userCouponInfo);
//            }
//            //新用户券通知记录
//            registMessage(couponInfo.getPrice(), userId, 2);
//            updateCount(2, couponInfo);
//        } else {
//            baseMapper.insert(userCouponInfo);
//            //新用户券通知记录
//            registMessage(couponInfo.getPrice(), userId, 1);
//            updateCount(1, couponInfo);
//        }
        return true;
    }

    private void registMessage(Integer price, Integer userId, Integer count) {
        try {
            String amount = FormatUtils.fenToYuan(price);
            Map<String, Object> params = new HashMap<>();
            params.put("amount", amount);
            params.put("count", count);
            Map<String, String> map = templateService.commonValue(params, MessageTemplate.REGIST_COUPON);
            noticeService.activieyCreateAndSave(map, userId, MessageNoticeType.activity_coupon.value());
        } catch (Exception e) {
            log.error("注册消息写入异常{}", e.getLocalizedMessage());
            e.printStackTrace();
        }
    }

//    /**
//     * 修改券的为已使用
//     *
//     * @param couponId
//     * @return
//     */
//    @Override
//    public Integer updateUsed(Integer couponId) {
//        return baseMapper.updateUsed(couponId);
//    }

    /**
     * 通过userCouponId修改优惠券的使用状态
     *
     * @param userCouponId
     * @param use
     * @return
     */
    @Override
    public Integer updateCouponUseById(Integer userCouponId, Integer use) {
        return baseMapper.updateCouponUseById2(userCouponId, use);

    }

//    /**
//     * 通过券id和用户id查询该用户未使用的券
//     *
//     * @param couponId
//     * @param id
//     * @return
//     */
//    @Override
//    public UserCouponInfo selectUserCoupon(Integer couponId, Integer id) {
//        return baseMapper.selectUserCoupon(couponId, id);
//    }


//    /**
//     * 用户领取券
//     *
//     * @param userId
//     * @param couponId
//     * @return
//     */
//    @Override
//    public Boolean userGetCoupon(Integer userId, Integer couponId) {
//        CouponInfo couponInfo = couponInfoMapper.selectById(couponId);
//        if (couponInfo == null) {
//            log.info("没有找到券couponId = {}", couponId);
//            throw new ServiceException("没有找到合适的券");
//        }
//        return getCoupon(couponInfo, userId);
//    }

    /**
     * 用戶領取推广券
     *
     * @param userId
     * @param couponValue
     * @param orderNo
     * @return
     */
    @Override
    public Boolean getRecommendCoupon(Integer userId, Integer couponValue, String orderNo) {
        CouponInfo couponInfo = null;
        if (couponValue == 20000) {
            couponInfo = couponInfoMapper.selectRecommendCoupon(CouponConst.free);
        } else {
            couponInfo = couponInfoMapper.selectRecommendCoupon(CouponConst.recommend);
        }
        if (couponInfo == null) {
            log.info("没有找到合适的推广券couponValue = {}", couponValue);
            throw new ServiceException("没有找到推广券");
        }
        return getRecommendCoupon(couponInfo, userId, couponValue, orderNo);
    }

    /**
     * 用户领取鱼宝兑换券
     *
     * @param userId
     * @param couponId
     * @return
     */
    @Override
    public Boolean userGetFishBallCoupon(Integer userId, Integer couponId) {
        CouponInfo couponInfo = couponInfoMapper.selectById(couponId);
        if (couponInfo == null) {
            log.info("没有找到券couponId = {}", couponId);
            throw new ServiceException("没有找到合适的券");
        }
        if (CouponConst.fishBall != couponInfo.getType()) {
            log.info("非鱼宝兑换券");
            throw new ServiceException("非鱼宝兑换券");
        }
        return getCoupon(couponInfo, userId);

    }

    /**
     * 获取推广充值获取的优惠券
     *
     * @param userInfo
     * @return
     */
    @Override
    public UserCouponInfoDTO selectNewCoupon(UserInfo userInfo) {
        UserCouponInfoDTO userCouponInfoDTO = new UserCouponInfoDTO();
        try {
            UserCouponInfo userCouponInfo = baseMapper.selectByNewest(userInfo.getId());
            if (userCouponInfo == null || DateUtils.dateCompareDate(DateUtils.nowAfterMinuteToDate(-1), userCouponInfo.getGmtCreate())) {
                log.info("充值获取的优惠券为空userId = {}", userInfo.getId());
                return null;
            }
            CouponInfo couponInfo = couponInfoMapper.selectById(userCouponInfo.getCouponId());
            if (couponInfo == null) {
                return null;
            }
            if (!couponInfo.getType().equals(CouponConst.recommend)
                    && !couponInfo.getType().equals(CouponConst.free)) {
                return null;
            }
            userCouponInfoDTO.setOverTime(userCouponInfo.getOverTime());
            userCouponInfoDTO.setTitle(couponInfo.getTitle());
            userCouponInfoDTO.setPrice(userCouponInfo.getCouponValue());
            userCouponInfoDTO.setValidFee(couponInfo.getValidFee());
            userCouponInfoDTO.setType(couponInfo.getType());
        } catch (Exception e) {
            log.error("获取推广充值获取的优惠券异常{}", e.getLocalizedMessage());
            e.printStackTrace();
            return null;
        }
        return userCouponInfoDTO;
    }

    /**
     * 通过主键修改用户领取券
     *
     * @param userCouponInfo
     */
    @Override
    public Integer updateByKey(UserCouponInfo userCouponInfo) {
        userCouponInfo.setGmtModified(new Date());
        return baseMapper.updateById(userCouponInfo);
    }

    /**
     * 查询券是否可用
     *
     * @param id
     */
    @Override
    public void checkCouponById(Integer id) {
        UserCouponInfo userCouponInfo = baseMapper.selectById(id);
        if (userCouponInfo == null) {
            throw new ServiceException("该券不存在");
        }
        if (UserCouponInfoStatus.USE.value().equals(userCouponInfo.getUsed())) {
            throw new ServiceException("该优惠券已被使用");
        }
        if (UserCouponInfoStatus.LOCK.value().equals(userCouponInfo.getUsed())) {
            throw new ServiceException("该优惠券已被锁定");
        }
        if (DateUtils.dateCompareDate(new Date(), userCouponInfo.getOverTime())) {
            throw new ServiceException("该优惠券已过期");
        }
    }

//    @Value("#{${CustomProperties.coupon.SmallBlackFishSpree}}")
//    private Map<Integer, Integer> smallBlackFishSpreeAmountConfig;

    /**
     * 给用户发大礼包优惠券(满300减30券2张、满200减20券4张、满100减10券6张)
     *
     * @param userId
     * @param orderNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void giveBagGift(Integer userId, String orderNo) {
        Map<String, Integer> map = configService.getGiftCouponAmount();
        redisUtil.runWithLock(redisUserCouponOperating, () -> {
            //判断订单号是否已生成订单,已生成就不生成
            List<UserCouponInfo> userCouponInfosOld = userCouponInfoMapper.selectList(new QueryWrapper<UserCouponInfo>().eq(UserCouponInfo.GIVE_ORDER_NO, orderNo));
            if (null == userCouponInfosOld || userCouponInfosOld.size() < 1) {
                //查询礼包券列表
                List<CouponInfo> couponInfos = couponInfoMapper.selectList(new QueryWrapper<CouponInfo>().eq(CouponInfo.TYPE, CouponConst.bagGift).eq(CouponInfo.STATUS, CouponConst.statusGetting));
                //添加券的集合
                ArrayList<UserCouponInfo> userCouponInfos = new ArrayList<>();
                //遍历
                for (CouponInfo couponInfo : couponInfos) {
                    //初步校验
                    checkCoupon(couponInfo);
                    //获取券发放的数量
                    Integer amount = map.get(couponInfo.getValidFee()) == null ? 0 : map.get(couponInfo.getValidFee());
                    if (amount > 0) {
                        //判断和数量校验
                        Integer remainCount = couponInfo.getRemainCount();
                        if (amount > remainCount) {
                            throw new ServiceException("礼包已被抢光了!");
                        }
                        Integer getCount = couponInfo.getGetCount();
                        couponInfo.setRemainCount(remainCount - amount);
                        couponInfo.setGetCount(getCount + amount);
                        //发放券数量
                        for (int i = 0; i < amount; i++) {
                            //将券对象添加到待添加集合
                            Date date = new Date();
                            UserCouponInfo userCouponInfo = new UserCouponInfo();
                            userCouponInfo.setGmtCreate(date);
                            userCouponInfo.setGmtModified(date);
                            userCouponInfo.setOverTime(DateUtils.afterDaysToDate(date, Long.valueOf(couponInfo.getDuration())));
                            userCouponInfo.setUserId(userId);
                            userCouponInfo.setCouponId(couponInfo.getId());
                            userCouponInfo.setCouponValue(couponInfo.getPrice());
                            userCouponInfo.setGiveOrderNo(orderNo);
                            userCouponInfo.setSource(UserCouponInfoSource.BAG_GIFT.value());
                            userCouponInfo.setUsed(UserCouponInfoStatus.WAIT_GET.value());
                            userCouponInfos.add(userCouponInfo);
                        }
                        //更新券数量
                        int update = couponInfoMapper.update(couponInfo, new UpdateWrapper<CouponInfo>().eq(CouponInfo.ID, couponInfo.getId()).eq(CouponInfo.REMAIN_COUNT, remainCount).eq(CouponInfo.GET_COUNT, getCount));
                        //判断失败回滚
                        if (update != 1) {
                            throw new ServiceException("服务器繁忙,请稍后再试");
                        }
                    }
                }
                //存储券
                saveBatch(userCouponInfos);
                //发送订单检查延迟队列
//                mqProducerService.sendDelayExchangeRechargeBiggit(orderNo);
            }
            return null;
        });
    }

    /**
     * 推广大礼包查询
     *
     * @param userId
     * @param orderNo
     * @return
     */
    @Override
    public List<BigGiftCouponListDTO> bagGiftCouponList(Integer userId, String orderNo) {
        return baseMapper.bigGiftCouponList(userId, orderNo);
    }

    /**
     * 充值发放大礼包券成功
     *
     * @param orderNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void giveSmallBlackFishSpreeSuccess(String orderNo) {
        //查询
        List<UserCouponInfo> userCouponInfos = list(new QueryWrapper<UserCouponInfo>()
                .eq(UserCouponInfo.GIVE_ORDER_NO, orderNo).eq(UserCouponInfo.USED, UserCouponInfoStatus.WAIT_GET.value()).eq(UserCouponInfo.SOURCE, UserCouponInfoSource.BAG_GIFT.value()));
        if (null != userCouponInfos && userCouponInfos.size() > 0) {
            Integer userId = userCouponInfos.get(0).getUserId();
            //判断是否已经有券
            List<UserCouponInfo> userCouponInfosUsed0 = list(new QueryWrapper<UserCouponInfo>()
                    .eq(UserCouponInfo.USER_ID, userId).eq(UserCouponInfo.SOURCE, UserCouponInfoSource.BAG_GIFT.value()).in(UserCouponInfo.USED, UserCouponInfoStatus.NO_USE.value(), UserCouponInfoStatus.USE.value(), UserCouponInfoStatus.LOCK.value()));
            //如果已经发券,中断操作
            if (null != userCouponInfosUsed0 && userCouponInfosUsed0.size() > 0) {
                if (!configService.checkWhiteList(userId)) {
                    log.warn("黑鱼大礼包:存在用户重复领取礼包:id为:{}", userId);
                    return;
                }
            }
            //修改状态
            for (UserCouponInfo userCouponInfo : userCouponInfos) {
                userCouponInfo.setUsed(UserCouponInfoStatus.NO_USE.value());
            }
            //更新
            updateBatchById(userCouponInfos);
        }
    }

    /**
     * 充值发放大礼包券失败
     *
     * @param orderNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void giveSmallBlackFishSpreeFail(String orderNo) {
        Map<String, Integer> map = configService.getGiftCouponAmount();
        redisUtil.runWithLock(redisUserCouponOperating, () -> {
            //查询
            List<UserCouponInfo> userCouponInfos = list(new QueryWrapper<UserCouponInfo>()
                    .eq(UserCouponInfo.GIVE_ORDER_NO, orderNo).eq(UserCouponInfo.USED, UserCouponInfoStatus.WAIT_GET.value()).eq(UserCouponInfo.SOURCE, UserCouponInfoSource.BAG_GIFT.value()));
            //修改状态
            for (UserCouponInfo userCouponInfo : userCouponInfos) {
                userCouponInfo.setUsed(UserCouponInfoStatus.DELETE.value());
            }
            //更新
            updateBatchById(userCouponInfos);
            //查询礼包券列表
            List<CouponInfo> couponInfos = couponInfoMapper.selectList(new QueryWrapper<CouponInfo>().eq(CouponInfo.TYPE, CouponConst.bagGift));
            //回滚券数量
            for (CouponInfo couponInfo : couponInfos) {
                //获取券发放的数量
                Integer amount = map.get(couponInfo.getValidFee()) == null ? 0 : map.get(couponInfo.getValidFee());
                Integer remainCount = couponInfo.getRemainCount();
                Integer getCount = couponInfo.getGetCount();
                couponInfo.setRemainCount(remainCount + amount);
                couponInfo.setGetCount(getCount - amount);
                //更新券数量
                int update = couponInfoMapper.update(couponInfo, new UpdateWrapper<CouponInfo>().eq(CouponInfo.ID, couponInfo.getId()).eq(CouponInfo.REMAIN_COUNT, remainCount).eq(CouponInfo.GET_COUNT, getCount));
            }
            return null;
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized UserCouponPushDTO selectCouponPush(UserCouponPushParam param) {

        UserCouponPushDTO couponPushDTO = null;
        //根据当前时间查询Push活动
        List<UserCouponPush> list = userCouponPushService.list(new QueryWrapper<UserCouponPush>()
                .lt(UserCouponPush.PUSH_START_TIME, new Date())
                .gt(UserCouponPush.PUSH_END_TIME, new Date())
                .orderByDesc(UserCouponPush.ID));
        if (!CollectionUtils.isEmpty(list)) {
            UserCouponPush couponPush = list.get(0);
            String key = RedisKey.COUPON_PUSH_KEY_USER_ID + couponPush.getId();
            LinkedHashMap map = (LinkedHashMap) redisUtil.hget(key);
            if (this.isObjectNotEmpty(map.get(String.valueOf(param.getUserId())))) {
                //查询优惠券
                List<Integer> couponIds = JSON.parseArray(couponPush.getCouponIdJson(), Integer.class);
                List<CouponInfo> couponInfos = couponInfoMapper.selectList(new QueryWrapper<CouponInfo>()
                        .in(CouponInfo.ID, couponIds));
                String loginIsKey = RedisKey.COUPON_PUSH_BY_LOGIN + param.getUserId();

                //检查是否调用过,如果调用过，直接返回NULL
                Object o = redisUtil.get(loginIsKey);
                if (isObjectNotEmpty(o)) {
                    return null;
                }

                //添加优惠券
                if (!CollectionUtils.isEmpty(couponInfos)) {
                    List<UserCouponInfo> userCouponInfoList = Lists.newArrayList();
                    //修改优惠券数量(如果是同一条数据，需要加锁)
                    for (CouponInfo couponInfo : couponInfos) {
                        if (couponInfo.getRemainCount() == 0) {
                            log.info("优惠券剩余数量为0");
                            return null;
                        }
                        couponInfo.setGetCount(couponInfo.getGetCount() + 1);
                        couponInfo.setRemainCount(couponInfo.getRemainCount() - 1);
                        couponInfoMapper.updateById(couponInfo);
                    }
                    for (CouponInfo couponInfo : couponInfos) {
                        UserCouponInfo userCouponInfo = new UserCouponInfo();
                        userCouponInfo.setCouponId(couponInfo.getId());
                        userCouponInfo.setUserId(param.getUserId());
                        userCouponInfo.setOverTime(couponInfo.getValidEnd());
                        userCouponInfo.setGmtCreate(new Date());
                        userCouponInfo.setGmtModified(new Date());
                        //普通后台发券
                        userCouponInfo.setSource(0);
                        userCouponInfo.setUsed(0);
                        userCouponInfo.setCouponValue(couponInfo.getPrice());
                        userCouponInfoList.add(userCouponInfo);
                    }
                    boolean b = super.saveBatch(userCouponInfoList);
                    if (b) {
                        long time = couponPush.getPushEndTime().getTime() - System.currentTimeMillis();
                        redisUtil.set(loginIsKey, JSON.toJSONString(couponInfos), (time / 1000));
                        //返回结果
                        couponPushDTO = ObjectConvertUtil.convertObject(couponPush, UserCouponPushDTO.class);
                        //塞入优惠券
                        assert couponPushDTO != null;
                        couponPushDTO.setList(this.convertCouponDTO(userCouponInfoList));
                        return couponPushDTO;
                    }
                }
            }

        }
        return null;
    }

    /**
     * 优惠券使用次数
     *
     * @param userId
     * @return
     */
    @Override
    public Integer selectCouponUseCountByUserId(Integer userId) {
        Integer count = baseMapper.selectCount(new QueryWrapper<UserCouponInfo>()
                .eq(UserCouponInfo.USER_ID, userId)
                .in(UserCouponInfo.USED, 1, 2));
        return null == count ? 0 : count;
    }

    /**
     * DTO转换
     *
     * @param userCouponInfoList
     * @return
     */
    private List<UserCouponInfoDTO> convertCouponDTO(List<UserCouponInfo> userCouponInfoList) {

        List<UserCouponInfoDTO> dtoList = Lists.newArrayList();

        if (!CollectionUtils.isEmpty(userCouponInfoList)) {

            UserCouponInfoDTO userCouponInfoDTO = null;
            for (UserCouponInfo userCouponInfo : userCouponInfoList) {
                userCouponInfoDTO = new UserCouponInfoDTO();
                CouponInfo couponInfo = couponInfoMapper.selectById(userCouponInfo.getCouponId());
                if (couponInfo == null) {
                    continue;
                }
                userCouponInfoDTO.setOverTime(userCouponInfo.getOverTime());
                userCouponInfoDTO.setTitle(couponInfo.getTitle());
                userCouponInfoDTO.setPrice(userCouponInfo.getCouponValue());
                userCouponInfoDTO.setValidFee(couponInfo.getValidFee());
                userCouponInfoDTO.setType(couponInfo.getType());
                dtoList.add(userCouponInfoDTO);
            }
        }
        return dtoList;
    }

    private Boolean isObjectNotEmpty(Object obj) {
        String str = ObjectUtils.toString(obj, "");
        return StringUtils.isNotBlank(str);
    }

    /**
     * 检查券的有效性
     *
     * @param couponInfo
     */
    private void checkCoupon(CouponInfo couponInfo) {
        if (DateUtils.dateCompareDate(new Date(), couponInfo.getValidEnd())) {
            log.info("满" + FormatUtils.fenToYuan(couponInfo.getValidFee()) + "减" + FormatUtils.fenToYuan(couponInfo.getPrice()) + "的券不在有效期");
            throw new ServiceException("券不在有效期");
        }
        if (couponInfo.getRemainCount() <= 0) {
            log.info("满" + FormatUtils.fenToYuan(couponInfo.getValidFee()) + "减" + FormatUtils.fenToYuan(couponInfo.getPrice()) + "的券已被领完");
            throw new ServiceException("券已被领取完");
        }
        if (!couponInfo.getStatus().equals(CouponConst.statusGetting)) {
            log.info("满" + FormatUtils.fenToYuan(couponInfo.getValidFee()) + "减" + FormatUtils.fenToYuan(couponInfo.getPrice()) + "的券不在发行期");
            throw new ServiceException("券不在发行期");
        }
    }

    private Boolean getCoupon(CouponInfo couponInfo, Integer userId) {
        if (DateUtils.dateCompareDate(new Date(), couponInfo.getValidEnd())) {
            log.info("券不在有效期");
            throw new ServiceException("券不在有效期");
        }
        if (!couponInfo.getStatus().equals(CouponConst.statusGetting)) {
            log.info("券不在发行期");
            throw new ServiceException("券不在发行期");
        }
        UserCouponInfo userCouponInfo = getUserCouponInfo(userId, couponInfo.getId(), couponInfo.getDuration());
        userCouponInfo.setCouponValue(couponInfo.getPrice());
        int res = baseMapper.insert(userCouponInfo);
        if (res != 1) {
            log.info("增加券失败{}{}", couponInfo.getId(), userId);
            return false;
        }
        //减少券总数
        Integer remainCount = couponInfo.getRemainCount();
        remainCount = remainCount - 1 < 0 ? 0 : remainCount - 1;
        //已得到的券
        int getCount = couponInfo.getGetCount() == null ? 0 : couponInfo.getGetCount();
        couponInfo.setRemainCount(remainCount);
        couponInfo.setGetCount(getCount + 1);
        int coup = 0;
        try {
            lock.writeLock().lock();
            couponInfo.setGmtModified(new Date());
            coup = couponInfoMapper.updateById(couponInfo);
        } finally {
            lock.writeLock().unlock();
        }

        return coup == 1;
    }

    private Boolean getRecommendCoupon(CouponInfo couponInfo, Integer userId, Integer couponValue, String orderNo) {
        if (DateUtils.dateCompareDate(new Date(), couponInfo.getValidEnd())) {
            log.info("券不在有效期");
            throw new ServiceException("券不在有效期");
        }
        if (!couponInfo.getStatus().equals(CouponConst.statusGetting)) {
            log.info("券不在发行期");
            throw new ServiceException("券不在发行期");
        }
        if (couponInfo.getRemainCount() == null || couponInfo.getRemainCount() == 0) {
            log.info("券已领取完毕");
            throw new ServiceException("非常抱歉，该券已领取完毕");
        }
        UserCouponInfo userCouponInfo = recommendUserCouponInfo(userId, couponInfo.getId(), couponInfo.getDuration(), couponValue, orderNo);
        int res = baseMapper.insert(userCouponInfo);
        if (res != 1) {
            log.info("增加券失败{}{}", couponInfo.getId(), userId);
            return false;
        }
        //减少券总数
        Integer remainCount = couponInfo.getRemainCount();
        remainCount = remainCount - 1 < 0 ? 0 : remainCount - 1;
        //已得到的券
        int getCount = couponInfo.getGetCount() == null ? 0 : couponInfo.getGetCount();
        couponInfo.setRemainCount(remainCount);
        couponInfo.setGetCount(getCount + 1);
        couponInfo.setGmtModified(new Date());
        int coup = couponInfoMapper.updateById(couponInfo);
        return coup == 1;
    }

    private UserCouponInfo recommendUserCouponInfo(Integer userId, Integer couponId, Integer duration, Integer couponValue, String orderNo) {
        UserCouponInfo userCouponInfo = new UserCouponInfo();
        userCouponInfo.setCouponValue(couponValue);
        userCouponInfo.setCouponId(couponId);
        userCouponInfo.setUserId(userId);
        userCouponInfo.setUsed(0);
        if (duration == null || duration == 0) {
            //180天
            duration = 180;
        }
        Date date = DateUtils.afterDaysToDate(new Date(), Long.valueOf(duration));
        userCouponInfo.setOverTime(date);
        userCouponInfo.setSource(UserCouponInfoSource.RECOMMEND.value());
        userCouponInfo.setOrderNo(orderNo);
        Date nowTime = new Date();
        userCouponInfo.setGmtCreate(nowTime);
        userCouponInfo.setGmtModified(nowTime);
        return userCouponInfo;
    }

    //领券,无通知
    @Override
    public void giveTypeCoupon(Integer userId, Integer type){
        //查询券
        List<CouponInfo> couponInfos = couponInfoMapper.selectList(
                new QueryWrapper<CouponInfo>()
                        .eq(CouponInfo.TYPE, type)
                        .eq(CouponInfo.STATUS, 2)
        );
        //遍历券
        if (CollectionUtil.isNotEmpty(couponInfos)) {
            for (CouponInfo couponInfo : couponInfos) {
                //判断能不能发
                if (couponInfo.getRemainCount() == null || couponInfo.getRemainCount() <= 0) {
                    log.info("券剩余数量不足:{}", type);
                    continue;
                }
                //查询该用户是否已经领取过该券
                UserCouponInfo userCouponInfo = baseMapper.selectOne(
                        new QueryWrapper<UserCouponInfo>()
                                .eq(UserCouponInfo.USER_ID, userId)
                                .eq(UserCouponInfo.COUPON_ID, couponInfo.getId())
                                .last("limit 1")
                );
                if (Objects.nonNull(userCouponInfo)) {
                    log.info("已领取过该券userId = {},coupon = {}", userId, JSONUtil.toJsonStr(couponInfo));
                    continue;
                }
                userCouponInfo = getUserCouponInfo(userId, couponInfo.getId(), couponInfo.getDuration());
                userCouponInfo.setCouponValue(couponInfo.getPrice());
                baseMapper.insert(userCouponInfo);
                //修改券数量
                updateCount(1, couponInfo);
            }
        }
    }


}
